import { isFormData } from "./index";

export interface BaseOptions {
	baseUrl: string;
	timeout?: number;
	headers?: any;
}
export interface RequestOptions {
	url: string;
	method: string;
	headers?: any;
	params?: any;
	credentials?: string;
	mode?: string;
	cache?: string;
	redirect?: string;
	referrer?: string;
}

export type RequestOptionsOther = Omit<RequestInit, "body" | "method">;

/**
 * 实现目标：
 * 1. 统一处理请求参数
 * 2. 统一处理返回结果
 * 3. 统一处理错误
 * 4. 超时处理
 * 5. 缓存处理
 * 6. 取消请求
 * 7. 重试机制
 * 8. 并发请求
 * 9. 流式处理
 */

/**
 * 根据请求方法处理参数
 * @param url
 * @param method 请求方法 GET | POST | PUT | DELETE | PATCH | HEAD | OPTIONS | CONNECT | TRACE
 * @param params 请求参数
 * @param headers 请求头
 * @returns
 */
function handleParams(method: string, params: any, headers: any) {
	method = method.toUpperCase();
	const result: {
		body: string | FormData | URLSearchParams | null;
		url: string;
	} = {
		body: null,
		url: "",
	};
	switch (method) {
		case "GET":
		case "DELETE": // 根据后端设置，目前默认放到url后面
		case "HEAD":
			result.url = "?" + new URLSearchParams(params).toString();
			break;
		case "POST":
			if (headers["Content-Type"] === "application/x-www-form-urlencoded") {
				result.body = new URLSearchParams(params);
			} else if (headers["Content-Type"] === "multipart/form-data" && isFormData(params)) {
				result.body = params;
			} else {
				result.body = JSON.stringify(params);
			}
			break;
		case "PUT":
		case "PATCH":
		case "TRACE":
			result.body = JSON.stringify(params);
			break;
		default:
			break;
	}

	return result;
}

/**
 * 处理状态码
 * @param status 状态码 400-600 之间的整数
 * @returns 状态码对应的状态描述
 */
const handleStatusCode = (status: number) => {
	switch (status) {
		case 400:
			return "请求错误";
		case 401:
			return "未授权，请登录";
		case 403:
			return "拒绝访问";
		case 404:
			return "请求错误，未找到该资源";
		case 405:
			return "请求方法未允许";
		case 408:
			return "请求超时";
		case 429:
			return "请求次数过多";
		case 500:
			return "服务器错误";
		case 502:
			return "网关错误";
		case 503:
			return "服务不可用";
		case 504:
			return "网关超时";
		default:
			return "未知错误";
	}
};

/**
 * 业务码处理
 * @param code 业务码
 * @returns 业务码对应的状态描述
 */

const handleBusinessCode = (code: number) => {
	switch (code) {
		case 200:
			return "成功";
		case 501:
			return "失败";
		default:
			return "未知错误";
	}
};

/**
 * 响应体处理
 * @param response 响应体
 * @returns
 */
const handleResponse = async (response: Response) => {
	if (response.ok) {
		// 200-299 成功
		const data = await response.json();
		// 业务码处理
		const message = handleBusinessCode(data.code);
		if (message !== "成功") {
			return Promise.reject(message);
		}
		return data?.data;
	} else {
		// 400-499 客户端错误 处理
		const message = handleStatusCode(response.status);
		return Promise.reject(message);
	}
};

/**
 * 处理TypeError错误
 * @param message
 */
const dnsErrorMessages = [
	"cannot resolve",
	"host not found",
	"name not resolved",
	"dns_lookup",
	"err_name_not_resolved",
	"dns",
];
const refusedIndicators = ["refused", "reset", "econnrefused", "econnreset", "connection refused"];

function handleTypeError(message: string) {
	message = message.toLowerCase();
	if (
		message.includes("failed to fetch") ||
		message.includes("network request failed") ||
		message.includes("fetch failed")
	) {
		if (message.includes("ssl") || message.includes("certificate")) {
			console.log("SSL证书错误");
		} else if (message.includes("cors") || message.includes("origin")) {
			console.log("CORS错误");
		} else if (dnsErrorMessages.some((item) => message.includes(item))) {
			console.log("DNS解析错误");
		} else {
			console.log("网络错误:", message);
		}
	} else if (message.includes("timeout") || message.includes("timed out")) {
		console.log("请求超时");
	} else if (message.includes("offline") || message.includes("network") || navigator.onLine === false) {
		console.log("无网络连接");
	} else if (refusedIndicators.some((item) => message.includes(item))) {
		console.log("连接被拒绝");
	} else {
		console.log("网络错误:", message);
	}
}

class RequestService {
	baseUrl: string;
	timeout: number;
	headers: any;
	constructor(options: BaseOptions) {
		this.baseUrl = options.baseUrl;
		this.timeout = options.timeout ?? 5000;
		this.headers = options.headers ?? {
			"Content-Type": "application/json",
		};
	}
	async request(options: RequestOptions) {
		const url = this.baseUrl + options.url;
		const requestOptions: RequestInit = {
			method: options.method,
			headers: {
				...this.headers,
				...options.headers,
			},
		};
		const { body, url: paramsUrl } = handleParams(options.method, options.params, requestOptions.headers);
		Object.assign(requestOptions, {
			body,
		});
		try {
			const response = await fetch(url + paramsUrl, requestOptions);
			return handleResponse(response);
		} catch (error) {
			// 网络错误/连接错误
			const { message, name } = error as Error;
			if (name === "AbortError") {
				console.log("请求被取消");
			} else if (name === "TimeoutError") {
				console.log("请求超时");
			} else if (name === "TypeError") {
				handleTypeError(message);
			} else {
				console.log(message);
			}
		}
	}

	get(url: string, params: Record<string, any> = {}, options: RequestOptionsOther = {}) {
		return this.request({
			...options,
			url,
			method: "GET",
			params,
		});
	}
	post(url: string, params: Record<string, any> = {}, options: RequestOptionsOther = {}) {
		return this.request({
			...options,
			url,
			method: "POST",
			params,
		});
	}
	put(url: string, params: Record<string, any> = {}, options: RequestOptionsOther = {}) {
		return this.request({
			...options,
			url,
			method: "PUT",
			params,
		});
	}
	delete(url: string, params: Record<string, any> = {}, options: RequestOptionsOther = {}) {
		return this.request({
			...options,
			url,
			method: "DELETE",
			params,
		});
	}
}

export default RequestService;
